using Engine.Graphics;
using FileHelper;
using System;
using System.IO;
using WorldRacing2.Material;

namespace WorldRacing2.Scenario.WR2TerrainLoader
{
    public struct WR2Surface
    {
        #region Public Fields

        public WorldRacing2.Material.MappingsWR2 mappings1;
        public WorldRacing2.Material.MappingsWR2 mappings2;
        public WorldRacing2.Material.MappingsWR2 mappings3;
        public short texture1ID;
        public short texture2ID;
        public short texture3ID;

        #endregion Public Fields

        #region Internal Fields

        internal short mappingMode;

        #endregion Internal Fields

        #region Private Fields

        private int hashcode;
        private long positionInFile;

        #endregion Private Fields

        #region Public Constructors

        public WR2Surface(Wr2Material material, MaterialManager materialManager)
        {
            this.texture1ID = (short)materialManager.GetTextureID(material.TextureName1);
            this.texture2ID = (short)materialManager.GetTextureID(material.TextureName2);
            this.texture3ID = (short)materialManager.GetTextureID(material.TextureName3);
            this.mappingMode = (short)material.MappingMode;
            this.mappings1 = material.Mappings1;
            this.mappings2 = material.Mappings2;
            this.mappings3 = material.Mappings3;
            this.positionInFile = 0;
            this.hashcode = 0;
            UpdateHashCode();
        }

        public WR2Surface(short texture1, short texture2, short texture3, EnumMappingMode mappingMode,
            WorldRacing2.Material.MappingsWR2 mappings1,
            WorldRacing2.Material.MappingsWR2 mappings2,
            WorldRacing2.Material.MappingsWR2 mappings3)
        {
            this.texture1ID = texture1;
            this.texture2ID = texture2;
            this.texture3ID = texture3;
            this.mappingMode = (short)mappingMode;
            this.mappings1 = mappings1;
            this.mappings2 = mappings2;
            this.mappings3 = mappings3;
            this.positionInFile = 0;
            this.hashcode = 0;
            UpdateHashCode();
        }

        public WR2Surface(short texture1, short texture2, short texture3, short mappingMode,
            WorldRacing2.Material.MappingsWR2 mappings1,
            WorldRacing2.Material.MappingsWR2 mappings2,
            WorldRacing2.Material.MappingsWR2 mappings3)
        {
            this.texture1ID = texture1;
            this.texture2ID = texture2;
            this.texture3ID = texture3;
            this.mappingMode = mappingMode;
            this.mappings1 = mappings1;
            this.mappings2 = mappings2;
            this.mappings3 = mappings3;
            this.positionInFile = 0;
            this.hashcode = 0;
            UpdateHashCode();
        }

        #endregion Public Constructors

        #region Public Methods

        public static WR2Surface FromStream(BinaryReader br)
        {
            WR2Surface surface = new WR2Surface(
                br.ReadInt16(),
                br.ReadInt16(),
                br.ReadInt16(),
                br.ReadInt16(),
                WorldRacing2.Material.MappingsWR2.FromStream(br),
                WorldRacing2.Material.MappingsWR2.FromStream(br),
                WorldRacing2.Material.MappingsWR2.FromStream(br));

            surface.positionInFile = br.BaseStream.Position;

            br.BaseStream.Position += 3 * 4;
            //surface.mappings1.CRC = br.ReadUInt32();
            //surface.mappings2.CRC = br.ReadUInt32();
            //surface.mappings3.CRC = br.ReadUInt32();
            return surface;
        }

        public override int GetHashCode()
        {
            return this.hashcode;
        }

        public void SaveToStream(BinaryWriter bw)
        {
            bw.Write(this.texture1ID);
            bw.Write(this.texture2ID);
            bw.Write(this.texture3ID);
            bw.Write(this.mappingMode);

            this.mappings1.SaveToStream(bw);
            this.mappings2.SaveToStream(bw);
            this.mappings3.SaveToStream(bw);

            bw.Write(this.mappings1.GetHashCode());
            bw.Write(this.mappings2.GetHashCode());
            bw.Write(this.mappings3.GetHashCode());

            //bw.Write(this.mappings1.GetHashCode());
            //bw.Write(this.mappings2.GetHashCode());
            //bw.Write(this.mappings3.GetHashCode());

            // Workaround. Because CRC often the same.
            //bw.Write(WR2Surface.crc);
            //WR2Surface.crc++;
            //bw.Write(WR2Surface.crc);
            //WR2Surface.crc++;
            //bw.Write(WR2Surface.crc);
            //WR2Surface.crc++;
        }

        public Wr2Material ToMaterial(MaterialManager materialManager)
        {
            if (this.texture1ID > materialManager.TextureNames.Count - 1)
                this.texture1ID = 0;
            if (this.texture2ID > materialManager.TextureNames.Count - 1)
                this.texture2ID = 0;
            if (this.texture3ID > materialManager.TextureNames.Count - 1)
                this.texture3ID = 0;
            int oldHashcode = this.hashcode;
            UpdateHashCode();

            if (oldHashcode != this.hashcode)
                throw new Exception("hascode is calculated new.");

            return new Wr2Material(materialManager.TextureNames[(int)this.texture1ID], materialManager.TextureNames[(int)this.texture2ID],
                materialManager.TextureNames[(int)this.texture3ID], (EnumMappingMode)this.mappingMode, materialManager, this.mappings1, this.mappings2, this.mappings3);
        }

        #endregion Public Methods

        #region Private Methods

        private void UpdateHashCode()
        {
            AdlerChecksum adler = new AdlerChecksum();
            byte[] byteArray = new byte[20];
            BitConverter.GetBytes(this.texture1ID).CopyTo(byteArray, 0);
            BitConverter.GetBytes(this.texture2ID).CopyTo(byteArray, 2);
            BitConverter.GetBytes(this.texture3ID).CopyTo(byteArray, 4);
            BitConverter.GetBytes(this.mappingMode).CopyTo(byteArray, 6);
            BitConverter.GetBytes(this.mappings1.GetHashCode()).CopyTo(byteArray, 8);
            BitConverter.GetBytes(this.mappings2.GetHashCode()).CopyTo(byteArray, 12);
            BitConverter.GetBytes(this.mappings3.GetHashCode()).CopyTo(byteArray, 16);

            adler.MakeForBuff(byteArray);
            this.hashcode = (int)adler.ChecksumValue;
        }

        #endregion Private Methods
    }
}